import { Injectable } from "@nestjs/common";
import { ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, registerDecorator } from "class-validator";
import { DataSource, ObjectType } from "typeorm";
import * as merge from 'deepmerge';
import { isNil } from 'lodash'
type Condition = {
    entity: ObjectType<any>;
    /**
     * 如果没有指定字段则使用当前验证的属性作为查询依据
     */
    property?: string;
};


@ValidatorConstraint({ name: 'isUnique', async: true })
@Injectable()
export class UniqueConstraint implements ValidatorConstraintInterface {
    constructor(
        protected dataSource: DataSource

    ) { }
    async validate(value: any, arg?: ValidationArguments){
        let config: Omit<Condition, "entity"> = {
            property: arg.property
        } 
        //合并对象
        const condition = ("entity" in arg.constraints[0] ? merge(config, arg.constraints[0]) : { ...config, entity: arg.constraints[0] }) as Required<Condition>
        //如果合并之后还没有
        if (!condition.entity) return false
        try {
            let Repo = this.dataSource.getRepository(condition.entity)
            let item = Repo.findOne({ where: { [condition.property]: value }, withDeleted: true })
            return isNil(item)
        } catch (error) {
            return false
        }

    }
    defaultMessage(args: ValidationArguments) {
        const { entity, property } = args.constraints[0];
        const queryProperty = property ?? args.property;
        if (!(args.object as any).getManager) {
            return 'getManager function not been found!';
        }
        if (!entity) {
            return 'Model not been specified!';
        }
        return `${queryProperty} of ${entity.name} must been unique!`;
    }
}

/**
 * 数据唯一性验证
 * @param params Entity类或验证条件对象
 * @param validationOptions
 */
export function IsUnique(
    params: ObjectType<any> | Condition,
    validationOptions?: ValidationOptions,
) {
    return (object: Record<string, any>, propertyName: string) => {
        registerDecorator({
            target: object.constructor,
            propertyName,
            options: validationOptions,
            constraints: [params],
            validator: UniqueConstraint,
        });
    };
}